package logic

import (
	"errors"
	"fmt"
	"gitee.com/freedom_feel/gui-bos-api/cmd/initialize/client"
	"gitee.com/freedom_feel/gui-bos-api/common"
	_type "gitee.com/freedom_feel/gui-bos-api/common/type"
	"gitee.com/freedom_feel/gui-bos-api/global"
	"gitee.com/freedom_feel/gui-bos-api/logs"
	"gitee.com/freedom_feel/gui-bos-api/models"
	"github.com/bytedance/sonic"
	"github.com/go-rod/rod"
	"github.com/go-rod/rod/lib/utils"
	"github.com/xuri/excelize/v2"
	"strconv"
	"strings"
	"time"
)

func FindAllData(address string) (all []models.SelectBoss, err error) {
	if address != "" {
		logs.Info("需要筛选：" + address)
		all, err = client.FindAreaData(address)
		if err != nil {
			return nil, err
		}
		if len(all) != 0 {
			return all, err
		} else {
			return nil, errors.New("未查询到该地区数据")
		}
	} else {
		logs.Info("查全部：" + address)
		all, err = client.FindAll()
		if err != nil {
			return nil, err
		}
		if len(all) == 0 {

		}
		return all, err
	}
}

func InsertCity(cityName, cityCode string) (err error) {
	err = client.Insert(cityName, cityCode)
	if err != nil {
		return err
	}
	return nil
}

func GetData(cd models.PmCityDB) (err error, data interface{}, flag bool) {
	// 如果有数据，直接返回
	RdWorkData, Rderr := client.RedisGet(cd.WorkCity)
	if Rderr != nil {
		logs.Info("查询redis失败,查询mysql...")
		res, err1 := client.FindAreaData(cd.WorkCity)
		if err1 != nil {
			return err1, nil, false
		}
		if len(res) != 0 {
			return nil, res, true
		}
	} else {
		var boosData []models.Boss
		logs.Info("查询redis正常,结果返回...")
		err = sonic.Unmarshal([]byte(RdWorkData), &boosData)
		if err != nil {
			return err, nil, false
		}
		return nil, boosData, true
	}
	return nil, nil, false
}

func FindOne(cd models.PmCityDB) (err error, data interface{}, flag bool) {
	// 如果有数据，直接返回
	RdWorkData, Rderr := client.RedisGet(cd.WorkCity)
	if Rderr != nil {
		logs.Info("查询redis失败,查询mysql...")
		res, err1 := client.FindAreaData(cd.WorkCity)
		if err1 != nil {
			return err1, nil, false
		}
		if len(res) != 0 {
			flag = true
			return nil, res, flag
		}
	} else {
		var boosData []models.Boss
		logs.Info("查询redis正常,结果返回...")
		err = sonic.Unmarshal([]byte(RdWorkData), &boosData)
		if err != nil {
			return err, nil, false
		}
		return nil, boosData, flag
	}

	// 使用协程并发执行任务
	code, OneErr := client.FindOneAreaCode(cd.WorkCity)
	if OneErr != nil {
		return OneErr, nil, false
	}

	if client.RedisExist("Bd_" + cd.WorkCity) {
		return errors.New("此任务正在执行中,请勿频繁发起"), nil, false
	}

	ch := make(chan models.Area, 5) // 创建带缓冲区的通道
	ch <- code                      // 向通道写入数据

	// 开启三个协程
	for i := 1; i <= 3; i++ {
		global.Wg.Add(1)
		go func(id int, ch chan models.Area, cd models.PmCityDB) {
			defer global.Wg.Done()
			FuncBFileDB(ch, id, cd)
		}(i, ch, cd)
	}

	// 等待所有 Goroutine 完成任务
	global.Wg.Wait()

	// 任务执行完毕后关闭管道
	close(ch)
	fmt.Println("协程任务为空")

	//var codeStrut modelss.Area
	//if v, ok := global.AreaMap[cd.WorkCity]; ok {
	//	codeStrut = modelss.Area{
	//		CityName: cd.WorkCity,
	//		CityCode: v,
	//	}
	//} else {
	//	err = errors.New("不存在该地区数据")
	//	return
	//}
	//
	//ch := make(chan modelss.Area, 5) // 创建带缓冲区的通道
	//ch <- codeStrut                 // 向通道写入数据
	//
	//for i := 1; i <= 3; i++ {
	//	// 开启三个协程
	//	global.Wg.Add(1)
	//	go func(id int, ch chan modelss.Area, cd modelss.PmCityDB) { defer global.Wg.Done(); FuncBFile(ch, id, cd) }(i, ch, cd)
	//}
	// 等待所有 Goroutine 完成任务
	//global.Wg.Wait()
	//fmt.Println("协程任务为空")

	return
}

func Del(brandName string) bool {
	if !client.Del(brandName) {
		return false
	}
	return true
}

func QueryTasks(name, key string) (models.Task, error) {
	var tk models.TaskKey
	taskKey := "taskkey_" + key
	gettaskKey, err := client.RedisGet(taskKey)
	sonic.Unmarshal([]byte(gettaskKey), &tk)
	if err != nil || tk.TaskKey == "" {
		queryTk, err := client.QueryTk(key)
		if err != nil {
			return models.Task{}, err
		}
		query, err := client.Query(queryTk.TaskKey)
		if err != nil {
			return models.Task{}, err
		}
		return query, nil
	}
	var t models.Task
	gettask, err := client.RedisGet(key)
	sonic.Unmarshal([]byte(gettask), &t)
	return t, nil
}

func EndTasks(name, key string) (models.Task, error) {
	global.EndChan = make(chan struct{}, 1)
	time.Sleep(3 * time.Second) // 让协程运行一段时间
	close(global.EndChan)       // 通过通道发送停止信号

	time.Sleep(1 * time.Second) // 等待协程退出

	//改变任务状态
	logs.Info("任务强制结束，更改任务状态...")
	err := client.UpdateTk(key, "任务强结束", nil)
	if err != nil {
		fmt.Println(err)
	}
	logs.Info("主函数结束")
	query, err := client.Query(key)
	if err != nil {
		return models.Task{}, err
	}
	return query, nil
}

func FuncBFileDB(ch chan models.Area, funcId int, cd models.PmCityDB) {
	// 从通道中读取数据
	one, ok := <-ch
	if !ok {
		logs.Info(fmt.Sprintf("Worker %v received signal to stop.", funcId))
		return
	}

	logs.Info(fmt.Sprintf("Worker %v received value: %v", funcId, one))
	logs.Info("城市名称： " + one.CityName)

	// 生成任务 ID 和 Key
	var taskid string
	for {
		taskid = common.RandomString(15)
		if !common.StartsWithZero(taskid) && taskid != "" {
			break
		}
	}
	key := taskid + fmt.Sprintf("%v", time.Now().Unix())
	taskname := "Bd" + "_" + one.CityName

	// 检查任务是否已存在
	if client.RedisExist(taskname) {
		logs.Info("此任务正在执行中,请勿频繁发起")
		return
	}

	// 将任务信息存入 Redis
	err := client.RedisSet(taskname, one.CityCode)
	if err != nil {
		logs.Error("RedisSet failed: " + err.Error())
		return
	}

	logs.Info("添加任务中...")
	logs.Info("任务KEY：" + key)
	logs.Info("redis task:" + key)

	// 创建任务对象
	tak := models.Task{
		TaskId:     taskid,
		TaskName:   taskname,
		TaskStatus: "任务执行中",
		TaskKey:    key,
		TaskData:   nil,
		NotifyUrl:  global.NotifyUrl,
	}

	// 将任务信息存入 Redis
	err = client.RedisSet(key, tak)
	if err != nil {
		logs.Error("RedisSet failed: " + err.Error())
		return
	}

	errTK := client.Transaction(taskname, key)
	if errTK != nil {
		logs.Error("Transaction failed: " + errTK.Error())
		return
	}

	logs.Info("添加任务完毕，执行任务中...")

	// 创建一个用于传递错误的通道
	errChan := make(chan models.ErrMsg, 1)
	defer close(errChan)

	// 执行任务
	BossFileDB(one.CityCode, taskname, errChan, funcId, one.CityName, cd.WorkType)

	logs.Info("执行任务结束，更改任务状态...")

	// 处理任务结果
	var data interface{}
	var res []models.SelectBoss
	ChanRes, ok := <-errChan
	if ok {
		if ChanRes.Err != nil && (ChanRes.Msg == "success" || ChanRes.Msg == "FailErr") {
			logs.Info(fmt.Sprintf("Error from worker %d: %v", funcId, ChanRes.Err))
			data = "该地区不支持数据查询或者浏览器启动错误"
		} else {
			var err1 error
			res, err1 = client.FindAreaData(one.CityName)
			if err1 != nil {
				logs.Error("FindAreaData failed: " + err1.Error())
				return
			}
			data = res[0]
		}
	}

	// 将任务结果存入数据库
	marshal, _ := sonic.Marshal(data)
	errtk := client.TransInsert(&models.Task{
		TaskId:     taskid,
		TaskName:   taskname,
		TaskStatus: "任务执行完成",
		TaskKey:    key,
		TaskData:   marshal,
		NotifyUrl:  global.NotifyUrl,
	})
	if errtk != nil {
		logs.Error("Transaction failed: " + errtk.Error())
		return
	}

	// 清理 Redis 缓存
	if !client.RedisDel(taskname) {
		logs.Info("任务缓存key删除失败")
	}
	if !client.RedisDel(key) {
		logs.Info("任务缓存key删除失败")
	}
	if !client.RedisDel("taskkey_" + key) {
		logs.Info("任务缓存key删除失败")
	}

	logs.Info("任务缓存key删除成功")
	logs.Info("任务执行完成")
}

func FuncBFile(ch chan models.Area, funcId int, cd models.PmCityDB) {
	// 从通道中读取数据
	task, ok := <-ch
	if !ok {
		logs.Info(fmt.Sprintf("Worker %v received signal to stop.", funcId))
		return
	}
	jsdata, _ := sonic.Marshal(task)
	logs.Info(fmt.Sprintf("Worker %v received value: %v", funcId, string(jsdata)))
	logs.Info(fmt.Sprintf("添加任务完毕，%v执行任务中...", funcId))

	// 创建一个用于传递错误的通道
	errChan := make(chan models.ErrMsg, 1)
	defer close(errChan)

	// 执行任务
	BossFile(task.CityCode, cd.WorkType, errChan)

	// 处理任务结果
	ChanRes, ok := <-errChan
	if ok {
		if ChanRes.Err != nil && (ChanRes.Msg == "success" || ChanRes.Msg == "FailErr") {
			logs.Info(fmt.Sprintf("Error from worker %d: %v", funcId, ChanRes.Err))
		}
	}

	logs.Info(fmt.Sprintf("%v任务执行完成", funcId))
}

func BossFileDB(cityCode, key string, errChan chan models.ErrMsg, id int, cityName, workType string) {
	defer func() {
		if r := recover(); r != nil {
			errChan <- models.ErrMsg{
				Err: fmt.Errorf("worker %d panicked: %v", id, r),
				Msg: "FailErr",
			}
		}
	}()
	var boosArray []models.Boss
	boosArray, err := RodGetPageData(common.NewBrowser(), key, workType, cityCode)
	if err != nil {
		errChan <- models.ErrMsg{
			Err: err,
			Msg: "FailErr",
		}
		return
	}
	err = client.RedisSet(cityName, boosArray)
	if err != nil {
		logs.Info(fmt.Sprintf("插入缓存失败:%v", err.Error()))
		errChan <- models.ErrMsg{
			Err: errors.New("插入缓存失败"),
			Msg: "FailErr",
		}
		return
	}
	err = WriteIntoXlsx(boosArray)
	if err != nil {
		errChan <- models.ErrMsg{
			Err: err,
			Msg: "FailErr",
		}
	}
	errChan <- models.ErrMsg{
		Err: nil,
		Msg: "success",
	}
	return

}

func BossFile(cityCode, WorkType string, errChan chan models.ErrMsg) {
	var boosArray []models.Boss
	boosArray, err := RodGetPageData(common.NewBrowser(), "", WorkType, cityCode)
	if err != nil {
		errChan <- models.ErrMsg{
			Err: err,
			Msg: "FailErr",
		}
		return
	}
	err = WriteIntoXlsx(boosArray)
	if err != nil {
		errChan <- models.ErrMsg{
			Err: err,
			Msg: "FailErr",
		}
		return
	}
	errChan <- models.ErrMsg{
		Err: nil,
		Msg: "success",
	}
	return

}

// 暂时也是固定的
func RodGetPageData(b *rod.Browser, key, WorkType, cityCode string) (boosArray []models.Boss, err error) {
	err = rod.Try(func() {
		var page *rod.Page
		isClose := false
		GwZzMap := make(map[string]models.JobDes)
		page = b.MustPage(global.BossUrl + fmt.Sprintf(`web/geek/job?query=%v&city=%v&page=%v`, WorkType, cityCode, "1"))
		time.Sleep(5 * time.Second)

		var num []string
		if page.MustHas(`[class="options-pages"]>a`) {
			for _, el := range page.MustElements(`[class="options-pages"]>a`) {
				num = append(num, el.MustText())
			}
		} else {
			page.MustClose()
			panic(errors.New("该地区未找到相应类型的工作"))
		}
		nump := 1
		pageNume := num[len(num)-2]
		logs.Info(_type.StrPinJie("总页数：", pageNume))
		logs.Info(_type.StrPinJie("总数据条数：", strconv.Itoa(common.StrToInt(pageNume)*len(page.MustElements(`[class="job-list-box"]>li`)))))
		if key != "" {
			if common.StrToInt(pageNume) > 5 {
				client.RedisTimeThree(key, cityCode)
			} else {
				client.RedisTimeTwo(key, cityCode)
			}
		}

		for i1 := 0; i1 < common.StrToInt(pageNume); i1++ {
			utils.Sleep(1.5)
			logs.Info(_type.StrPinJie("当前页数：", strconv.Itoa(nump)))
			nump++
			for _, element := range page.MustElementsX(`//ul[@class="job-list-box"]/li`) {
				var newp *rod.Page
				ss := page.MustWaitOpen()
				element.MustClick()
				utils.Sleep(5)
				newp = ss()
				newp.MustWaitDOMStable()
				//一公司名  为 key 进行筛选 详情 岗位职责
				companyName := element.MustElement(`[class="company-name"]`).MustText()
				gwzz := newp.MustElement(`[class="job-sec-text"]`).MustText()
				if len(gwzz) >= 150 {
					// 截取字符串至最多150个字符
					gwzz = common.TruncateString(gwzz, 150)
				}
				GwZzMap[companyName] = models.JobDes{
					GwZz: gwzz,
				}
				newp.MustClose()
			}
			for index, _ := range page.MustElements(`[class="job-list-box"]>li`) {
				index += 1
				var boos models.Boss
				boos.JobName = page.MustElementX(fmt.Sprintf(`//ul[@class="job-list-box"]/li[%v]/div[1]/a/div[1]/span[1]`, index)).MustText()
				boos.Address = page.MustElementX(fmt.Sprintf(`//ul[@class="job-list-box"]/li[%v]/div[1]/a/div[1]/span[2]`, index)).MustText()
				city := strings.Split(page.MustElementX(fmt.Sprintf(`//ul[@class="job-list-box"]/li[%v]/div[1]/a/div[1]/span[2]`, index)).MustText(), "·")
				boos.CityName = city[0]
				boos.SalaryDesc = page.MustElementX(fmt.Sprintf(`//ul[@class="job-list-box"]/li[%v]//a/div[2]/span`, index)).MustText()
				boos.JobExperience = page.MustElementX(fmt.Sprintf(`//ul[@class="job-list-box"]/li[%v]//a/div[2]//li[1]`, index)).MustText()
				boos.JobDegree = page.MustElementX(fmt.Sprintf(`//ul[@class="job-list-box"]/li[%v]//a/div[2]//li[2]`, index)).MustText()
				boos.BossName = page.MustElementX(fmt.Sprintf(`//ul[@class="job-list-box"]/li[%v]//a/div[2]//div`, index)).MustText()
				boos.BossTitle = page.MustElementX(fmt.Sprintf(`//ul[@class="job-list-box"]/li[%v]//a/div[2]//div//em`, index)).MustText()
				BrandName := page.MustElementX(fmt.Sprintf(`//ul[@class="job-list-box"]/li[%v]//div/div[2]//a`, index)).MustText()
				boos.BrandName = BrandName
				boos.BrandIndustry = page.MustElementX(fmt.Sprintf(`//ul[@class="job-list-box"]/li[%v]//div/div[2]//li[1]`, index)).MustText()
				boos.BrandStageName = page.MustElementX(fmt.Sprintf(`//ul[@class="job-list-box"]/li[%v]//div/div[2]//li[2]`, index)).MustText()
				if strings.Contains(page.MustElementX(fmt.Sprintf(`//ul[@class="job-list-box"]/li[%v]//div/div[2]//li[2]`, index)).MustText(), "人") {
					boos.BrandStageName = ""
					boos.BrandScaleName = page.MustElementX(fmt.Sprintf(`//ul[@class="job-list-box"]/li[%v]//div/div[2]//li[2]`, index)).MustText()
				} else {
					boos.BrandScaleName = page.MustElementX(fmt.Sprintf(`//ul[@class="job-list-box"]/li[%v]//div/div[2]//li[3]`, index)).MustText()
				}
				boos.WelfareList = page.MustElementX(fmt.Sprintf(`//ul[@class="job-list-box"]/li[%v]//div/div[2]//li[2]`, index)).MustText()
				var zwms string
				for _, v := range page.MustElementsX(fmt.Sprintf(`//ul[@class="job-list-box"]/li[%v]/div[2]//li`, index)) {
					if zwms == "" {
						zwms = v.MustText()
					} else {
						zwms += zwms + "," + v.MustText()
					}
				}
				if len(zwms) >= 255 {
					// 截取字符串至最多255个字符
					zwms = common.TruncateString(zwms, 255)
				}
				boos.JobDes = zwms
				res := GwZzMap[BrandName]
				boos.GwZz = res.GwZz
				boosArray = append(boosArray, boos)

				err = client.TransInsert(&boos)
				if err != nil {
					_type.PrintfLogErr(logs.Logerr, "插入数据失败", err.Error())
					_type.ErrChan <- *_type.ErrSystem("事务插入失败")
					return
				}
			}
			logs.Info("扫描的数据插入成功")
			//是否是最后一页
			class := *page.MustElementsX(`//div[@class="options-pages"]/a`).Last().MustAttribute("class")
			if strings.Contains(class, "disabled") {
				logs.Info("最后一页已执行完")
				isClose = true
				page.MustClose()
				break
			} else {
				page.MustElement(`[class="ui-icon-arrow-right"]`).MustScrollIntoView()
				page.MustElement(`[class="ui-icon-arrow-right"]`).MustClick()
			}
		}
		//}
		if !isClose {
			page.MustClose()
		}
	})
	if err != nil {
		if strings.Contains(err.Error(), "未找到") {
			return nil, err
		}
		return nil, _type.ErrSystem("获取数据出错或相应页面有变动")
	}
	return boosArray, nil
}

// 这个模版是固定
func WriteIntoXlsx(boosArray []models.Boss) error {
	row := 2 // 从第二行开始写入数据
	// 按城市名称分组写入数据
	cityFiles := make(map[string]*excelize.File)
	for _, item := range boosArray {
		if _, exists := cityFiles[item.CityName]; !exists {
			// 为新城市创建文件
			cityFiles[item.CityName] = excelize.NewFile()
			sheetName := "Sheet1"
			cityFiles[item.CityName].NewSheet(sheetName)
			// 设置标题行
			titles := []string{"工作名称", "城市名称", "文凭", "工作经验", "地址", "薪资", "招聘人", "招聘人职位", "招聘公司名称", "公司规模", "公司类型", "公司现状", "公司福利", "职位描述", "岗位职责"}
			for i, title := range titles {
				cell, _ := excelize.CoordinatesToCellName(i+1, 1)
				cityFiles[item.CityName].SetCellValue(sheetName, cell, title)
			}
		}
		// 写入数据
		cityFiles[item.CityName].SetCellValue("Sheet1", fmt.Sprintf("A%d", row), item.JobName)
		cityFiles[item.CityName].SetCellValue("Sheet1", fmt.Sprintf("B%d", row), item.CityName)
		cityFiles[item.CityName].SetCellValue("Sheet1", fmt.Sprintf("C%d", row), item.JobDegree)
		cityFiles[item.CityName].SetCellValue("Sheet1", fmt.Sprintf("D%d", row), item.JobExperience)
		cityFiles[item.CityName].SetCellValue("Sheet1", fmt.Sprintf("E%d", row), item.Address)
		cityFiles[item.CityName].SetCellValue("Sheet1", fmt.Sprintf("F%d", row), item.SalaryDesc)
		cityFiles[item.CityName].SetCellValue("Sheet1", fmt.Sprintf("G%d", row), item.BossName)
		cityFiles[item.CityName].SetCellValue("Sheet1", fmt.Sprintf("H%d", row), item.BossTitle)
		cityFiles[item.CityName].SetCellValue("Sheet1", fmt.Sprintf("I%d", row), item.BrandName)
		cityFiles[item.CityName].SetCellValue("Sheet1", fmt.Sprintf("J%d", row), item.BrandScaleName)
		cityFiles[item.CityName].SetCellValue("Sheet1", fmt.Sprintf("K%d", row), item.BrandIndustry)
		cityFiles[item.CityName].SetCellValue("Sheet1", fmt.Sprintf("L%d", row), item.BrandStageName)
		cityFiles[item.CityName].SetCellValue("Sheet1", fmt.Sprintf("M%d", row), item.WelfareList)
		cityFiles[item.CityName].SetCellValue("Sheet1", fmt.Sprintf("N%d", row), item.JobDes)
		cityFiles[item.CityName].SetCellValue("Sheet1", fmt.Sprintf("O%d", row), item.GwZz)
		row++
	}

	// 保存每个城市的文件
	for cityName, file := range cityFiles {
		fileName := fmt.Sprintf("%s.xlsx", cityName) // 文件名为城市名
		if err := file.SaveAs(fileName); err != nil {
			logs.Info(fmt.Sprintf("Error saving file for city %s: %v", cityName, err))
			return errors.New("Error saving file for city " + cityName)
		} else {
			defer file.Close()
			logs.Info(fmt.Sprintf("File saved for city %s", cityName))
			return nil
		}
	}
	return nil
}
